﻿#region Header

// Written by Tobias Lekman, 2008. For more information and contact details, see http://www.lekman.com.
// 
// File released under Microsoft Public License (Ms-PL). For full details see http://www.microsoft.com/opensource/licenses.mspx
// 
// Conditions and Limitations
// 
// (A) No Trademark License- This license does not grant you rights to use any contributors' name, logo, or trademarks.
// 
// (B) If you bring a patent claim against any contributor over patents that you claim are infringed by the software, your patent license from such contributor to the software ends automatically.
// 
// (C) If you distribute any portion of the software, you must retain all copyright, patent, trademark, and attribution notices that are present in the software.
// 
// (D) If you distribute any portion of the software in source code form, you may do so only under this license by including a complete copy of this license with your distribution. If you distribute any portion of the software in compiled or object code form, you may only do so under a license that complies with this license.
// 
// (E) The software is licensed "as-is." You bear the risk of using it. The contributors give no express warranties, guarantees or conditions. You may have additional consumer rights under your local laws which this license cannot change. To the extent permitted under your local laws, the contributors exclude the implied warranties of merchantability, fitness for a particular purpose and non-infringement. 

#endregion

#region Using Statements

using System;
using System.Globalization;
using System.IO;
using System.Text;
using System.Web.UI;
using Microsoft.SharePoint;
using Microsoft.SharePoint.Administration;

#endregion

namespace CodePlex.Lekman.SharePoint.Testing
{
    /// <summary>
    /// Base test fixture for working with the <see cref="SharePoint"/> API.
    /// </summary>
    public abstract class SharePointTestFixture : IDisposable
    {
        #region Fields

        private BrowserTestFixture browserTest;
        private bool isDisposed;
        private MockTestFixture mock;
        private SPSite site;
        private SPWeb web;

        #endregion

        #region Constructor

        /// <summary>
        /// Initializes a new instance of the <see cref="SharePointTestFixture"/> class.
        /// </summary>
        protected SharePointTestFixture() {}

        /// <summary>
        /// Initializes a new instance of the <see cref="SharePointTestFixture"/> class.
        /// </summary>
        /// <param name="siteNamePart">The wild card host name to match a site collection to.</param>
        protected SharePointTestFixture(string siteNamePart)
            : this(siteNamePart,
                   "/") {}

        /// <summary>
        /// Initializes a new instance of the <see cref="SharePointTestFixture"/> class.
        /// </summary>
        /// <param name="siteNamePart">The wild card host name to match a site collection to.</param>
        /// <param name="webRelativePath">The server relative URL of the <see cref="SPWeb"/> to use.</param>
        protected SharePointTestFixture(string siteNamePart,
                                        string webRelativePath)
        {
            SetContext(
                this,
                siteNamePart,
                webRelativePath);
        }

        #endregion

        #region Properties

        /// <summary>
        /// Gets an instance of the object used to perform automated browser tests.
        /// </summary>
        protected BrowserTestFixture BrowserTest
        {
            get
            {
                if (browserTest == null)
                {
                    browserTest = new BrowserTestFixture(this);
                }
                return browserTest;
            }
        }

        /// <summary>
        /// Gets an instance of the helper class used for mocking.
        /// </summary>
        protected MockTestFixture Mock
        {
            get
            {
                if (mock == null)
                {
                    mock = new MockTestFixture(this);
                }
                return mock;
            }
        }

        /// <summary>
        /// Gets the <see cref="SPSite"/> that contains the <see cref="Web"/> to test within.
        /// </summary>
        protected internal SPSite Site
        {
            get
            {
                if (site == null)
                {
                    site = new SPSite(SiteUrl);
                }
                return site;
            }
        }

        /// <summary>
        /// Gets the <see cref="SPWeb"/> that contains objects to test.
        /// </summary>
        protected internal SPWeb Web
        {
            get
            {
                if (web == null)
                {
                    web = Site.OpenWeb(WebUrl);
                }
                return web;
            }
        }

        /// <summary>
        /// Gets or sets the absolute URL of the <see cref="Site"/>.
        /// </summary>
        protected abstract string SiteUrl{get;set;}

        /// <summary>
        /// Gets or sets the server relative URL of the <see cref="Web"/>.
        /// </summary>
        protected abstract string WebUrl{get;set;}

        #endregion

        #region Methods

        /// <summary>
        /// Sets the <see cref="SharePointTestFixture.Site"/> and <see cref="SharePointTestFixture.Web"/> objects by retrieving a <see cref="SharePoint"/> site by wild card URL. This is useful for multiple farm scenarios where you have a development farm, test farm, stage farm and live farm. In this scenario, we will be able to bind against http://projectname.development.company.com, http://projectname.test.company.com and https://www.projectname.com.
        /// </summary>
        /// <param name="fixture">The <see cref="SharePointTestFixture"/> to set context parameters for.</param>
        /// <param name="siteNamePart">The wild card host name to match a site collection to.</param>
        protected static void SetContext(SharePointTestFixture fixture,
                                         string siteNamePart)
        {
            SetContext(fixture,
                       siteNamePart,
                       "/");
        }

        /// <summary>
        /// Sets the <see cref="SharePointTestFixture.Site"/> and <see cref="SharePointTestFixture.Web"/> objects by retrieving a <see cref="SharePoint"/> site by wild card URL. This is useful for multiple farm scenarios where you have a development farm, test farm, stage farm and live farm. In this scenario, we will be able to bind against http://projectname.development.company.com, http://projectname.test.company.com and https://www.projectname.com.
        /// </summary>
        /// <param name="fixture">The <see cref="SharePointTestFixture"/> to set context parameters for.</param>
        /// <param name="siteNamePart">The wild card host name to match a site collection to.</param>
        /// <param name="webRelativePath">The server relative URL of the <see cref="SPWeb"/> to use.</param>
        protected static void SetContext(SharePointTestFixture fixture,
                                         string siteNamePart,
                                         string webRelativePath)
        {
            SPWebService service = SPFarm.Local.Services.GetValue<SPWebService>(string.Empty);
            foreach (SPWebApplication application in service.WebApplications)
            {
                foreach (SPAlternateUrl url in application.AlternateUrls)
                {
                    if (url.Uri.Host.ToUpperInvariant().Contains(siteNamePart.ToUpperInvariant()))
                    {
                        fixture.SiteUrl = url.IncomingUrl;
                        fixture.WebUrl = webRelativePath;
                        return;
                    }
                }
            }
            throw new ArgumentException("No site collection found matching whole or part of the host name specified",
                                        "siteNamePart");
        }

        /// <summary>
        /// Renders the output of a control to a string.
        /// </summary>
        /// <param name="control">The control to render to string.</param>
        /// <returns>A string containing the HTML output of the control.</returns>
        protected static string RenderToString(Control control)
        {
            StringBuilder builder = new StringBuilder();
            StringWriter writer = new StringWriter(builder,
                                                   CultureInfo.InvariantCulture);
            HtmlTextWriter html = new HtmlTextWriter(writer);
            control.RenderControl(html);
            return builder.ToString();
        }

        #endregion

        #region Implementation of IDisposable

        /// <summary>
        /// Disposes the managed and unmanaged resources instantiated within the class.
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Disposes the managed and unmanaged resources instantiated within the class.
        /// </summary>
        /// <param name="disposing">Determines whether managed resources should be disposed.</param>
        protected virtual void Dispose(bool disposing)
        {
            if (!isDisposed)
            {
                if (disposing)
                {
                    if (web != null)
                    {
                        web.Dispose();
                    }
                    if (site != null)
                    {
                        site.Dispose();
                    }
                    if (browserTest != null)
                    {
                        browserTest.Dispose();
                    }
                    if (mock != null)
                    {
                        mock.Dispose();
                    }
                }
            }
            isDisposed = true;
        }

        #endregion
    }
}